package com.insigma.business.common.utils;

import com.insigma.business.common.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: MinIoUtil
 * @Description: 文件服务器工具类
 * @Author: chengk
 * @Date: 2024/03/22 16:53
 * @Version: v1.0
 */

@Slf4j
@Component
public class MinIoUtil {
    @Resource
    private MinioConfig minioConfigBean;

    @Resource
    private MinioClient minioClientBean;

    private static MinioConfig minioConfig;

    private static MinioClient minioClient;



    /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        minioConfig = minioConfigBean;
        minioClient = minioClientBean;
    }



    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(File file) throws Exception {
        return upload(file,null);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(File file,String bucketName) throws Exception {
        return upload(file,bucketName,null);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(File file,String bucketName,String filePath) throws Exception {
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        // 检查文件路径
        if (StringUtils.isBlank(filePath)) {
            filePath = file.getName();
        }
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            //上传文件
            PutObjectArgs objectArgs = PutObjectArgs.builder().object(filePath)
                    .bucket(bucketName)
                    .contentType("application/octet-stream")
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            System.out.println(minioConfig.getEndpoint() + bucketName + "/" + filePath);
//            return minioConfig.getEndpoint() + bucketName + "/" + filePath;
            return "/" + bucketName + "/"  + filePath;

        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 上传文件
     *
     * @param fileStr  base64编码
     * @param fileName 文件名称
     * @return 文件完整路径
     */
    public static String upload(String fileStr, String fileName) throws Exception {
        return upload(fileStr,fileName,null);
    }

    public static String upload(String fileStr, String fileName,String bucketName) throws Exception {
        return upload(fileStr,fileName,bucketName,null);
    }


    /**
     * 上传文件
     * @param fileByteArr 文件字节集
     * @param fileName 文件名称
     * @param bucketName
     * @param modelName 模块名称
     * @return
     * @throws Exception
     */
    public static String upload(byte[] fileByteArr, String fileName,String bucketName,String modelName) throws Exception {
        modelName = initModelName(modelName);
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        InputStream inputStream = new ByteArrayInputStream(fileByteArr);
        try {
            int idx = fileName.lastIndexOf(".");
            String tag = fileName.substring(idx);
            String prefix = fileName.substring(0, idx);
            String fName = prefix + "_" + TimeUtil.getDatePrimaryKey() + tag;
            //上传文件
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(modelName+fName)
                    .bucket(bucketName)
                    .contentType("application/octet-stream")
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            String endPoint = minioConfig.getEndpoint().endsWith("/") ? minioConfig.getEndpoint() : minioConfig.getEndpoint() +"/";
            System.out.println(endPoint + bucketName + "/" +modelName+ fName);
//            return endPoint + bucketName + "/" +modelName+ fName;
            return "/" + bucketName + "/" + modelName + fileName;

        } finally {
            inputStream.close();
        }
    }

    /**
     * 上传文件
     *
     * @param fileStr  base64编码
     * @param fileName 文件名称
     * @return 文件完整路径
     */
    public static String upload(String fileStr, String fileName,String bucketName,String modelName) throws Exception {
        modelName = initModelName(modelName);
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        String file = fileStr;
        if(fileStr.contains(",")){
            file = fileStr.split(",")[1];
        }
        log.info("[MinIoUtil --> upload]上传文件base64：{}", fileStr);
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] b = decoder.decodeBuffer(file);
        for (int i = 0; i < b.length; ++i) {
            // 调整异常数据
            if (b[i] < 0) {
                b[i] += 256;
            }
        }
        InputStream inputStream = new ByteArrayInputStream(b);
        try {
            int idx = fileName.lastIndexOf(".");
            String tag = fileName.substring(idx);
            String prefix = fileName.substring(0, idx);
            String fName = prefix + "_" + TimeUtil.getDatePrimaryKey() + tag;
            //上传文件
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(modelName+fName)
                    .bucket(bucketName)
                    .contentType("application/octet-stream")
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            String endPoint = minioConfig.getEndpoint().endsWith("/") ? minioConfig.getEndpoint() : minioConfig.getEndpoint() +"/";
            System.out.println(endPoint + bucketName + "/" +modelName+ fName);
//            return endPoint + bucketName + "/" +modelName+ fName;
            return "/" + bucketName + "/" + modelName + fileName;

        } finally {
            inputStream.close();
        }
    }

    /**
     * 上传文件
     *
     * @param inputStream 文件流
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(InputStream inputStream) throws Exception {
        return upload(inputStream,null);
    }

    /**
     * 上传文件
     *
     * @param inputStream 文件流
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(InputStream inputStream,String bucketName) throws Exception {
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        String fileName = TimeUtil.getDatePrimaryKey() + ".png";
        //上传文件
        PutObjectArgs objectArgs = PutObjectArgs.builder().object(fileName)
                .bucket(bucketName)
                .contentType("application/octet-stream")
                .stream(inputStream, inputStream.available(), -1).build();
        minioClient.putObject(objectArgs);
        System.out.println(minioConfig.getEndpoint() + bucketName + "/" + fileName);
//        return minioConfig.getEndpoint() + bucketName + "/" + fileName;
        return "/" + bucketName + "/" + fileName;

    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(MultipartFile file) throws Exception {
        return upload(file,null,null);
    }

    /**
     * 上传文件
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String upload(MultipartFile file,String bucketName,String modelName) throws Exception {
        modelName = initModelName(modelName);
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            String name = file.getOriginalFilename();
            int idx = name.lastIndexOf(".");
            String tag = name.substring(idx);
            String fileName = TimeUtil.getDatePrimaryKey() + tag;
            //上传文件
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(modelName+fileName)
                    .bucket(bucketName)
                    .contentType("application/octet-stream")
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            String endPoint = minioConfig.getEndpoint().endsWith("/") ? minioConfig.getEndpoint() : minioConfig.getEndpoint() +"/";
            System.out.println(endPoint + bucketName + "/" + modelName + fileName);
//            return endPoint + bucketName + "/" +modelName+ fileName;
            return "/" + bucketName + "/" + modelName + fileName;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 上传文件 保留中文名称
     *
     * @param file 文件
     * @return 文件完整路径
     * @throws Exception
     */
    public static String uploadRetainChName(MultipartFile file, String bucketName, String modelName) throws Exception {
        modelName = initModelName(modelName);
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        checkBucket(bucketName);
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            String originalFilename = file.getOriginalFilename();
            String tag = originalFilename.substring(originalFilename.lastIndexOf("."));
            String fileName = originalFilename.replace(tag, "") + "-" + TimeUtil.getDatePrimaryKey() + tag;
            //上传文件
            PutObjectArgs objectArgs = PutObjectArgs.builder()
                    .object(modelName+fileName)
                    .bucket(bucketName)
                    .contentType("application/octet-stream")
                    .stream(inputStream, inputStream.available(), -1).build();
            minioClient.putObject(objectArgs);
            String endPoint = minioConfig.getEndpoint().endsWith("/") ? minioConfig.getEndpoint() : minioConfig.getEndpoint() +"/";
            System.out.println(endPoint + bucketName + "/" + modelName + fileName);
//            return endPoint + bucketName + "/" +modelName+ fileName;
            return "/" + bucketName + "/" + modelName + fileName;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    private static String initModelName(String modelName) {
        if (!StringUtils.isBlank(modelName)) {
            return modelName = modelName+"/";
        }
        return modelName;
    }

    public static String preViewFile(String filePath) {
        return preViewFile(filePath,null);
    }
        /**
         * 预览
         * @param filePath
         */
    public static String preViewFile(String filePath,String endpoint) {
        String[] s = filePath.split("/");
        String bucket = s[3];
        String fileName = "";
        for (int i = 4;i < s.length;i++) {
            if (i==4) {
                fileName = s[i];
            } else {
                fileName = fileName+"/"+s[i];
            }
        }
        Map<String, String> query = new HashMap<>(2, 1);
        query.put("filename", fileName);
        GetPresignedObjectUrlArgs pre = GetPresignedObjectUrlArgs.builder()
                .bucket(bucket)
                .object(fileName)
                .expiry(10, TimeUnit.MINUTES)
                .method(Method.GET)
                .extraQueryParams(query)
                .build();
        try {
            MinioClient newMinioClient = MinioClient.builder()
                    .endpoint(StringUtils.isBlank(endpoint)?minioConfig.getEndpoint():endpoint)
                    .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                    .build();
            return newMinioClient.getPresignedObjectUrl(pre);
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        }
    }


    public static InputStream downloadFile(String filePath) {
        return downloadFile(filePath,null);
    }

    public static InputStream downloadFile(String filePath,String bucketName) {
        //检查桶是否存在
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioConfig.getBucket();
        }
        try {
            GetObjectArgs objectArgs = GetObjectArgs.builder().object(filePath)
                    .bucket(bucketName).build();
            return minioClient.getObject(objectArgs);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    public static String downloadFileAsString(String filePath) {
        return downloadFileAsString(filePath,null);
    }

    public static String downloadFileAsString(String filePath,String bucketName) {
        InputStream inputStream = downloadFile(filePath,bucketName);
        try {
            ByteArrayOutputStream result = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                result.write(buffer, 0, length);
            }
            String str = result.toString(StandardCharsets.UTF_8.name());
            return str;
        } catch (Exception e) {
            log.info("下载文件失败：", e.getMessage());
            return "";
        }

    }

    private static void checkBucket(String bucket) throws Exception{
        //检查桶是否存在
        boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if (!isExist) {
            //不存在则创建
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }

    }

}
